#ifndef FRAMEWORK_H_
#define FRAMEWORK_H_

#include <algorithm>
#include <FLOAT.H>
#include <vector>
#include <queue>
#include <list>
#include <map>
#include <set>
#include "zthread/Thread.h"
#include "zthread/Runnable.h"
#include "zthread/Mutex.h"
#include "../Solution/Solution.h"
#include "../Util/SolutionPtrComparator.h"
#include "Worker/FrameworkWorker.h"

using namespace std;

class Worker;
class EvaluateFitnessWorker;
class CrossoverWorker;
class LocalEvaluateFitnessWorker;
class LocalCrossoverWorker;
class CrossoverJob;
class EvaluateFitnessJob;
class Environment;
class Selection;
class Crossover;

/** 
 * The framework class manages all the components of the genetic algorithm.
 */
class Framework
{
    public:
        /** 
         * Constructor. Detailed here.
         **/
        Framework(Environment*, Crossover*, Selection*, map<string, double>&);
        vector<Solution*> getBestSolutions();                                                                        /**< Returns a vector containing the best solutions that have been found */
        void getFitnessHistory(list<double> &avgHistory, list<double> &topHistory, unsigned int &generationNumber);  /**< Populates the provided containers with fitness history information  */
        void getBriefFitnessHistory(double &avgFitness, double &topFitness, unsigned int &generationNumber);  /**< Populates the provided containers with fitness history information  */
        void start();                                                                                                /**< Starts the genetic algorithm (non-blocking)                         */
        void join();                                                                                                 /**< ??                                                                  */
        void stop();                                                                                                 /**< Stops the genetic algorithm                                         */
        void limitedRun(int);                                                                                        /**< Runs for the indicated number of generations (blocking)             */
        void registerWorker(CrossoverWorker*);                                                                       /**< Registers a new crossover worker thread                             */
        void registerWorker(EvaluateFitnessWorker*);                                                                 /**< Registers a new evaluate fitness worker thread                      */
        void unregisterWorker(CrossoverWorker*);                                                                     /**< Unregisters a crossover worker thread                               */
        void unregisterWorker(EvaluateFitnessWorker*);                                                               /**< Unregisters an evaluate fitness worker thread                       */
        void handleWorkerDone(CrossoverWorker* const);                                                               /**< Function to let the framework know a  crossover job is done         */
        void handleWorkerDone(EvaluateFitnessWorker* const);                                                         /**< Function to let the framework know an evaluate fitness job is done  */
        CrossoverJob *getCrossoverJob(CrossoverWorker*);                                                             /**< Function for crossover workers to get new jobs                      */
        EvaluateFitnessJob *getEvaluateFitnessJob(EvaluateFitnessWorker*);                                           /**< Function for evaluate fitness workers to get a new jobs             */
        ~Framework();
        
    private:
        friend class FrameworkWorker;
        FrameworkWorker* frameworkWorker;
        
        void preloadBestSolutions();
        void preloadHistory();
        void preloadEvaluateFitnessJobs();
        
        list<CrossoverJob*> unassignedCrossoverJobs;
        ZThread::Mutex unassignedCrossoverJobsMutex;
        
        list<EvaluateFitnessJob*> unassignedEvaluateFitnessJobs;
        ZThread::Mutex unassignedEvaluateFitnessJobsMutex;
        
        unsigned int completedEvaluateFitnessJobs;
        ZThread::Mutex completedEvaluateFitnessJobsMutex;
        
        set<EvaluateFitnessWorker*> evaluateFitnessWorkers;
        ZThread::Mutex evaluateFitnessWorkersMutex;
        
        set<CrossoverWorker*> crossoverWorkers;
        ZThread::Mutex crossoverWorkersMutex;

        Selection* selector;
        Crossover* crosser;
        Environment* simulation;
        vector<Solution*> currentPopulation;
        
        ZThread::Mutex newPopulationMutex;
        vector<Solution*> newPopulation;
        
        ZThread::Mutex historyMutex;
        list<double> topFitnessHistory;
        list<double> avgFitnessHistory;
        
        ZThread::Mutex bestSolutionsMutex;
        priority_queue< Solution*, vector<Solution*>, SolutionPtrGTComparator > bestSolutions;
        
        unsigned int generationCount;
        unsigned int targetGenerationCount;
        unsigned int topResultsSize;
        unsigned int populationSize;
        unsigned int historySize;
        
        EvaluateFitnessWorker* evaluateFitnessWorker;
        ZThread::Thread* evaluateFitnessWorkerThread;
        
        CrossoverWorker* crossoverWorker;        
        ZThread::Thread* crossoverWorkerThread;
        
        ZThread::Thread* workerThread;
        bool limitedRunFlag;
};


#endif /*FRAMEWORK_H_*/
